package org.mozhu.mboot.core.i18n;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.context.MessageSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.core.Ordered;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;

/**
 * load i18n message source from database
 */
@Configuration
@ConditionalOnBean(MessageResourceLoader.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@EnableConfigurationProperties
@Slf4j
@Import(ValidationAutoConfiguration.class)
public class MessageSourceAutoConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "spring.messages")
    public MessageSourceProperties messageSourceProperties() {
        MessageSourceProperties messageSourceProperties = new MessageSourceProperties();
        messageSourceProperties.setBasename(null);
        return messageSourceProperties;
    }

    @Bean
    public MessageSource messageSource(MessageResourceLoader messageResourceLoader) {
        MessageSourceProperties properties = messageSourceProperties();
        ReloadableMessageSource reloadableMessageSource = new ReloadableMessageSource();
        reloadableMessageSource.setMessageResourceLoader(messageResourceLoader);
        reloadableMessageSource.setParentMessageSource(localMessageSource());
        reloadableMessageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
        Duration cacheDuration = properties.getCacheDuration();
        if (cacheDuration != null) {
            reloadableMessageSource.setCacheMillis(cacheDuration.toMillis());
        }
        return reloadableMessageSource;
    }

    public MessageSource localMessageSource() {
        MessageSourceProperties properties = messageSourceProperties();
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();

        if (StringUtils.hasText(properties.getBasename())) {
            messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
                    StringUtils.trimAllWhitespace(properties.getBasename())));
        }
        try {
            Set<String> baseNames = calculateBaseNamesByConvention();
            messageSource.addBasenames(baseNames.toArray(new String[0]));
            log.info("Load message resource by convention: [{}]", baseNames);
        } catch (IOException e) {
            log.warn("Load message resource by convention failed", e);
        }

        if (properties.getEncoding() != null) {
            messageSource.setDefaultEncoding(properties.getEncoding().name());
        }
        messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
        Duration cacheDuration = properties.getCacheDuration();
        if (cacheDuration != null) {
            messageSource.setCacheMillis(cacheDuration.toMillis());
        }
        messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
        messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
        return messageSource;
    }

    private Set<String> calculateBaseNamesByConvention() throws IOException {
        String messages = "messages";
        String messageResource = "messageResource";
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resolver.getResources("classpath*:" + messages + "/**/" + messageResource + "*.properties");
        Set<String> baseNames = new HashSet<>();
        if (resources != null && resources.length > 0) {
            for (Resource resource : resources) {
                String path = resource.getURL().getPath();
                int start = path.lastIndexOf(messages);
                int end = path.lastIndexOf(messageResource);
                String substring = path.substring(start, end + messageResource.length());
                baseNames.add(substring);
            }
        }
        return baseNames;
    }
    
}
